<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8" />
    <script>
        let { log } = console;

        ////////////////////////////////////////////////////////////////////////////////
        //  实现对象的克隆 end
        ////////////////////////////////////////////////////////////////////////////////

        ////////////////////////////////////////////////////////////////////////////////
        //  实现对象的深度克隆 start
        ////////////////////////////////////////////////////////////////////////////////
        //返回传递给他的任意对象的类
        const isClass = (o) => {
            if (o === null) return "Null";
            if (o === undefined) return "Undefined";
            // slice(start, end) 方法可提取字符串的某个部分，并以新的字符串返回被提取的部分。
            // 使用 start（包含） 和 end（不包含） 参数来指定字符串提取的部分。
            // start 参数字符串中第一个字符位置为 0, 第二个字符位置为 1, 以此类推，如果是负数表示从尾部截取多少个字符串，slice(-2) 表示提取原数组中的倒数第二个元素到最后一个元素（包含最后一个元素）。
            // end 参数如果为负数，-1 指字符串的最后一个字符的位置，-2 指倒数第二个字符，以此类推。
            return Object.prototype.toString.call(o).slice(8, -1);
        };

        const isNullAndUndefined = (value) => {
            let result = true;
            if (value != null && value != undefined) {
                result = false;
            }
            return result;
        };
        const isString = (value) => {
            let result = true;
            if (isNullAndUndefined(value) == false && isClass(value) != "String") {
                result = false;
            }
            return result;
        };
        const isObject = (value) => {
            let result = true;
            if (isNullAndUndefined(value) == false && isClass(value) != "Object") {
                result = false;
            }
            return result;
        };
        const isArray = (value) => {
            let result = true;
            if (isNullAndUndefined(value) == false && isClass(value) != "Array") {
                result = false;
            }
            return result;
        };
        const isNumber = (value) => {
            let result = true;
            if (isNullAndUndefined(value) == false && isClass(value) != "Number") {
                result = false;
            }
            return result;
        };
        const isBoolean = (value) => {
            let result = true;
            if (isNullAndUndefined(value) == false && isClass(value) != "Boolean") {
                result = false;
            }
            return result;
        };
        const isRegExp = (value) => {
            let result = true;
            if (isNullAndUndefined(value) == false && isClass(value) != "RegExp") {
                result = false;
            }
            return result;
        };
        const isDate = (value) => {
            let result = true;
            if (isNullAndUndefined(value) == false && isClass(value) != "Date") {
                result = false;
            }
            return result;
        };
        const isSymbol = (value) => {
            let result = true;
            if (isNullAndUndefined(value) == false && isClass(value) != "Symbol") {
                result = false;
            }
            return result;
        };
        const isFunction = (value) => {
            let result = true;
            if (isNullAndUndefined(value) == false && isClass(value) != "Function") {
                result = false;
            }
            return result;
        };
        ////////////////////////////////////////////////////////////////////////////////
        //  实现对象的深度克隆 start
        ////////////////////////////////////////////////////////////////////////////////
        //深度克隆
        const deepClone = function (obj) {
            let result;
            const oClass = isClass(obj);
            //确定result的类型
            if (oClass === "Object") {
                result = {};
            } else if (oClass === "Array") {
                result = [];
                // }else if (oClass === "Foundation") {
                //   result = new Foundation({});
                // }else if (oClass === "FoundationDetail") {
                //   result = [];
                // }else if (oClass === "FoundationPaiHang") {
                //   result = [];
                // }else if (oClass === "FoundationTongJi") {
                //   result = [];
                // }else if (oClass === "User") {
                //   result = [];
            } else {
                return obj;
            }
            ////////////////////////////////////////
            // 拷贝自身
            ////////////////////////////////////////
            // for...in循环遍历对象自身的和继承的可枚举属性（不含 Symbol 属性）。
            // for (key in obj) {
            // 拷贝自身属性和方法-》 Object.keys返回一个数组，包括对象自身的（不含继承的）所有可枚举属性（不含 Symbol 属性）的键名。
            // for (key of Object.keys(obj)) {

            // Reflect.ownKeys返回一个数组，包含对象自身的（不含继承的）所有键名，不管键名是 Symbol 或字符串，也不管是否可枚举。
            for (const key of Reflect.ownKeys(obj)) {
                const copy = obj[key];
                // if (
                //   isClass(copy) == "Object" ||
                //   isClass(copy) == "Foundation" ||
                //   isClass(copy) == "FoundationDetail" ||
                //   isClass(copy) == "FoundationPaiHang" ||
                //   isClass(copy) == "FoundationTongJi" ||
                //   isClass(copy) == "User"
                // )
                if (isClass(copy) == "Object") {
                    // result[key] = arguments.callee(copy); //递归调用
                    result[key] = deepClone(copy); //递归调用
                } else if (isClass(copy) == "Array") {
                    // result[key] = arguments.callee(copy);
                    result[key] = deepClone(copy); //递归调用
                } else {
                    // result[key] = copy;
                    // console.log("aaaa" + isClass(copy));
                    if (isNullAndUndefined(copy)) {
                        Reflect.defineProperty(result, key, {
                            value: copy,
                            writable: true,
                            enumerable: true,
                            configurable: true,
                        });
                    } else {
                        Reflect.defineProperty(
                            result,
                            key,
                            Reflect.getOwnPropertyDescriptor(
                                obj,
                                key
                            )
                        );
                    }
                    // Object.getOwnPropertyDescriptor(obj, key);
                }
            }
            ////////////////////////////////////////
            //拷贝原型
            ////////////////////////////////////////
            //拷贝原型的属性和方法-》 Object.keys返回一个数组，包括对象自身的（不含继承的）所有可枚举属性（不含 Symbol 属性）的键名。
            const protoObj = Reflect.getPrototypeOf(obj);
            if (protoObj == Array.prototype) {
                // 防止出现类数组对象
                Reflect.setPrototypeOf(result, Array.prototype);
            } else {
                Reflect.setPrototypeOf(result, {});
            }
            const resultProtoObj = Reflect.getPrototypeOf(result);
            //拷贝原型-》属性和方法
            if (isNullAndUndefined(protoObj) == false) {
                for (const key of Reflect.ownKeys(protoObj)) {
                    const copy = protoObj[key] || obj[key];// 防止class中的getter setter 原型无法读取值，所以要从实例属性上取下值
                    // if (
                    //   isClass(copy) == "Object" ||
                    //   isClass(copy) == "Foundation" ||
                    //   isClass(copy) == "FoundationDetail" ||
                    //   isClass(copy) == "FoundationPaiHang" ||
                    //   isClass(copy) == "FoundationTongJi" ||
                    //   isClass(copy) == "User"
                    if (isClass(copy) == "Object") {
                        // resultProtoObj[key] = arguments.callee(copy); //递归调用
                        resultProtoObj[key] = deepClone(copy); //递归调用
                    } else if (isClass(copy) == "Array") {
                        // resultProtoObj[key] = arguments.callee(copy);
                        resultProtoObj[key] = deepClone(copy); //递归调用
                    } else {
                        // resultProtoObj[key] = copy;
                        // console.log("bbbb" + isClass(copy));
                        if (isNullAndUndefined(copy)) {
                            Reflect.defineProperty(resultProtoObj, key, {
                                value: copy,
                                writable: true,
                                enumerable: true,
                                configurable: true,
                            });
                        } else {
                            Reflect.defineProperty(
                                resultProtoObj,
                                key,
                                Reflect.getOwnPropertyDescriptor(
                                    protoObj,
                                    key
                                )
                            );
                        }
                    }
                }
            }
            return result;
        };

        // var oPerson = {
        //     oName: "rookiebob",
        //     oAge: "18",
        //     oAddress: {
        //         province: "beijing"
        //     },
        //     ofavorite: [
        //         "swimming",
        //         { reading: "history book" }
        //     ],
        //     skill: function () {
        //         console.log("bob is coding");
        //     }
        // };
        // //深度克隆一个对象
        // var oNew = deepClone(oPerson);

        // oNew.ofavorite[1].reading = "picture";
        // console.log(oNew.ofavorite[1].reading);//picture
        // console.log(oPerson.ofavorite[1].reading);//history book

        // oNew.oAddress.province = "shanghai";
        // console.log(oPerson.oAddress.province);//beijing
        // console.log(oNew.oAddress.province);//shanghai
        ////////////////////////////////////////////////////////////////////////////////
        //  实现对象的深度克隆 end
        ////////////////////////////////////////////////////////////////////////////////


        // ////////////////////////////////////////////////////////////////////////////////
        // //  实现对象的克隆 start
        // ////////////////////////////////////////////////////////////////////////////////
        // let orginObj = { p1: "p1", [Symbol("p2")]: "p2" };
        // let sex = Symbol("sex");
        // let obj = {
        //     name: "szq", age: 11, [sex]: "man",
        //     get sex() { return this[sex]; },
        //     set sex(value) { this[sex] = value; },
        //     getName: function () { return this.name; }
        // };
        // console.log(Reflect.getOwnPropertyDescriptor(obj, "sex"));
        // Reflect.defineProperty(obj, "p0", { writable: true, enumerable: false, value: "p0", configurable: true });
        // Reflect.setPrototypeOf(obj, orginObj);

        // // 克隆方法1
        // // let target = Object.assign({}, obj);
        // // Reflect.setPrototypeOf(target, orginObj); // 克隆自身和原型的可枚举的属性 包括Symbol属性
        // // 克隆方法2
        // // let target = Object.assign(Object.create(Reflect.getPrototypeOf(obj)), obj) // 克隆自身和原型的可枚举的属性 包括Symbol属性
        // // 克隆方法3
        // // let target=JSON.parse(JSON.stringify(obj));// 只克隆自身可枚举的属性，不包括Symbol属性，也不包括原型的属性
        // // log(target);

        // // 克隆方法4 
        // // （深度克隆）对象自身包括原型
        // let target = deepClone(obj);
        // target.name = "123234asfasf";
        // Reflect.getPrototypeOf(target)["p1"] = "szqtarget";
        // log(obj);
        // log(target);
        class test0 {
            m_name = "szq";
            get myname() { return this.m_name; }
            set myname(value) { this.m_name = value; }
        }

        let test1 = new test0();
        let test2 = deepClone(test1);
        console.log(Reflect.ownKeys(test2));

        // console.log(Reflect.ownKeys(test1));
        // console.log(Reflect.ownKeys(Reflect.getPrototypeOf(test1)));

        // let test2 = {
        //     m_name: "szq",
        //     get myname() { return this.m_name; },
        //     set myname(value) { this.m_name = value; }
        // }
        // console.log(Reflect.ownKeys(test2));

    </script>
</head>

<body>

</body>

</html>